package it.softphone.commons.util;


import it.softphone.commons.service.LogAware;

import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.FileConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;

public class PasswordManager implements LogAware {

	
	public static final String PASSWORD_HIDDEN_PROPERTY = "password.hidden";

	private PasswordManager() {
	}


	public static String decode( String value ) throws IOException {
		if( null==value ) throw new IllegalArgumentException("value parameter is null!");
		
		return new String(Base64.decode(value.getBytes()));
		
	}
	
	/**
	 * Encode password within a .properties file 
	 * 
	 * Encode has performed only if there is a property 'password.hidden=true'
	 *  
	 * Encode has applyed only on properties that has postfix '.password'  
	 * 
	 * @param p source configuration file
	 * @return if encoding has been performed
	 * @throws ConfigurationException
	 */
	public static boolean encodePropertiesFile( FileConfiguration p ) throws ConfigurationException {
		if( null==p ) throw new IllegalArgumentException("properties file parameter is null!");
		
		boolean passwordHidden = p.getBoolean(PASSWORD_HIDDEN_PROPERTY, false);
		
		if( passwordHidden ) {
			encodeProperties(p);
	
			p.save();
		}

		return passwordHidden;
		
	}
	
	
	/**
	 * encode properties and remove the 'passwords hidden' property
	 * 
	 * @param properties
	 */
	public static void encodeProperties( Configuration properties ) {
		if( null==properties ) throw new IllegalArgumentException("properies  parameter is null!");
		
		@SuppressWarnings("unchecked")
		java.util.Iterator<Object> ki = properties.getKeys();
		
		while( ki.hasNext() ) {
		
			Object k = ki.next();
			
			if( k instanceof String ) {

				String kk = (String)k;
				
				if( kk.contains(".password")) {

					String enc = Base64.encodeBytes(properties.getString(kk).getBytes() );
					
					logger.debug(String.format( "encode property [%s]\n", kk));
					
					properties.setProperty(kk, enc );
				}
			}
		}
		
		properties.clearProperty( PASSWORD_HIDDEN_PROPERTY );
		
	}
		
	/**
	 * Encode password within a .properties file 
	 * 
	 * Encode has performed only if there is a property 'password.hidden=true'
	 *  
	 * Encode has applyed only on properties that has postfix '.password'  
	 * 
	 * 
	 * 
	 * @Deprecated use encodePropertiesFile(FileConfiguration) instead
	 * @param propertiesFile file where encode password
	 * @throws FileNotFoundException
	 * @return if encoding has been performed
	 * @throws IOException
	 * @see {@link #encodePropertiesFile(FileConfiguration)}
	 */
	@Deprecated
	public static boolean encodePropertiesFile( java.io.File propertiesFile ) throws FileNotFoundException, IOException {
		if( null==propertiesFile ) throw new IllegalArgumentException("properties file parameter is null!");
		
		java.util.Properties p = new java.util.Properties();
		
		p.load( new java.io.FileInputStream(propertiesFile));

		boolean passwordHidden = Boolean.valueOf(p.getProperty(PASSWORD_HIDDEN_PROPERTY, "false"));
		
		if( passwordHidden ) {
			encodeProperties(p);
			p.store( new java.io.FileOutputStream(propertiesFile), "passwords hidden");
		}

		return passwordHidden;
		
	}
	
	
	/**
	 * encode properties and remove the 'passwords hidden' property
	 * 
	 * @param properties
	 */
	public static void encodeProperties( java.util.Properties properties ) {
		if( null==properties ) throw new IllegalArgumentException("properies  parameter is null!");
		
		for( Object k : properties.keySet() ) {
		
			if( k instanceof String ) {

				String kk = (String)k;
				
				if( kk.contains(".password")) {

					String enc = Base64.encodeBytes(properties.getProperty(kk).getBytes() );
					
					logger.debug(String.format( "encode property [%s]\n", kk));
					
					properties.setProperty(kk, enc );
				}
			}
		}
		
		properties.remove( PASSWORD_HIDDEN_PROPERTY );
		
	}
	
	/**
	 * 
	 * @param properties
	 * @return if decoding has been performed
	 * @throws IOException 
	 */
	public static boolean decodeProperties( java.util.Properties properties ) throws IOException  {
		if( null==properties ) throw new IllegalArgumentException("properties parameter is null!");
		
		boolean decodeProperties = Boolean.parseBoolean( properties.getProperty(PASSWORD_HIDDEN_PROPERTY, "true"));	
		if( !decodeProperties ) return decodeProperties;

		for( Object k : properties.keySet() ) {
		
			if( k instanceof String ) {

				String kk = (String)k;
				
				if( kk.contains(".password")) {

					byte[] dec = Base64.decode(properties.getProperty(kk));
					
					properties.setProperty(kk, new String(dec) );
				}
			}
		}
		
		return decodeProperties;
	}
	
	/**
	 * 
	 * @param properties
	 * @return if decoding has been performed
	 * @throws IOException 
	 */
	@SuppressWarnings("unchecked")
	public static boolean decodeProperties( Configuration properties ) throws IOException  {
		if( null==properties ) throw new IllegalArgumentException("properties parameter is null!");

		boolean decodeProperties = properties.getBoolean(PASSWORD_HIDDEN_PROPERTY, true);
		
		if( !decodeProperties ) return decodeProperties;
		
		java.util.Iterator<String> keys = properties.getKeys();
		

		while( keys.hasNext() ) {
		
			String kk = keys.next();
			
			if( kk.contains(".password")) {

				byte[] dec = Base64.decode(properties.getString(kk));
				
				properties.setProperty(kk, new String(dec) );
			}
		}
		
		return decodeProperties;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		
		if( args.length != 1 ) {
			System.out.printf( "usage: java -jar audit-security-<version>.jar <file name>.properties\n");
		}
		
		java.io.File file = new java.io.File( args[0] );
		
		System.out.printf( "start encoding password for file [%s]\n", file.getAbsolutePath() );

		PropertiesConfiguration p = new PropertiesConfiguration(file);

		encodePropertiesFile(p);

		System.out.printf( "encoding complete\n" );
		
		decodeProperties(p);
		
		p.save(System.out);
	}

}
